import SerializeAble, { n2c, RegClass } from "./serialize";
import { Subject } from "./subject";
import Utils, { Random } from "./utils";

export type bool = 0 | 1;
export type NetData = number | string | bool | boolean | {} | NetData[];
export type AttrData = number | string | bool | boolean;
export type AttrDataType = "" | "number" | "string" | "boolean" | "bool";

export type Collection<T> = { [key: string]: T };
export type Col<T> = Collection<T>;

export type Ele = HTMLElement;
export type EleDiv = HTMLDivElement;
export type EleBtn = HTMLButtonElement;
export type EleInput = HTMLInputElement;
export type EleTextArea = HTMLTextAreaElement;
export type EleSpan = HTMLSpanElement;
export type EleCanvas = HTMLCanvasElement;
export type EleA = HTMLLinkElement;
export type EleSelect = HTMLSelectElement;
export type EleForm = HTMLFormElement;
export type EleIFrame = HTMLIFrameElement;
export type EleImage = HTMLImageElement;
export type EleLi = HTMLLIElement;
export type EleUl = HTMLUListElement;
export type EleTable = HTMLTableElement;


export abstract class CloneAble {
    abstract clone(): CloneAble;
};

export class ResourceReferce<T> {
    protected _ref = 1;
    get ref() {
        return this._ref;
    }
    set ref(val) {
        if (this._ref === val) {
            return;
        }
        this._ref = val;
        if (this._ref <= 0) {
        }
    }

    hasRef() {
        return this._ref > 0;
    }
    res: T = null;

    constructor(res: T) {
        this.res = res;
    }
};

export class AttrChanged {
    property = "";
    old: any;
    value: any;
};

export function CreateSubjectCollectionProxy(
    subject: Subject
): any {
    return new Proxy({}, {
        get: (target, property, receiver) => {
            if (property in target) {
                return Reflect.get(target, property, receiver);
            }
            return undefined;
        },
        set: (target, property, value, receiver) => {
            let old = Utils.DeepClone(target[property]);
            const success = Reflect.set(target, property, value, receiver);
            let msg = new AttrChanged();
            //@ts-ignore
            msg.property = property;
            msg.old = old;
            msg.value = value;
            subject.emit("changed", msg);
            return success;
        },
        deleteProperty: (target, property) => {
            return Reflect.deleteProperty(target, property);
        },
        // Other traps can be added as needed
    });
}

@RegClass("ConfigAble")
export class ConfigAble extends SerializeAble {
    static configList: any[] = [];
    protected static _configListInit = false;

    static async Init(fileList: string[]) {
        if (this._configListInit) {
            return;
        }
        for (let i = 0; i < fileList.length; i++) {
            let res = await fetch(`${fileList[i]}.json`);
            let json = await res.json();
            this.configList = this.configList.concat(json);
        }
        this._configListInit = true;
    }

    static CreateRandom<T extends ConfigAble>(filterCb: (conf: T) => boolean = null) {
        let ctor = n2c(this["$cn"]);
        let obj: T = new ctor();
        let list: any[] = this.configList.filter(ele => ele.$cn === this["$cn"]);
        if (filterCb) {
            list = list.filter(filterCb);
        }
        obj.assignFromJSON(Random.selecter(...list))
        return obj;
    }
    static CreateByName(name: string) {
        let ctor = n2c(this["$cn"]);
        let obj = new ctor();
        obj.assignFromJSON(this.configList.filter(ele => ele.$cn === this["$cn"]).find(ele => ele.name === name));
        return obj;
    }
};